package com.example.jiaoyidui.component;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.example.jiaoyidui.entity.ApiRequestParam;
import com.example.jiaoyidui.entity.excel.ResponseExcel;
import static com.example.jiaoyidui.util.ThreadPoolUtil.getThreadPool;

import com.example.jiaoyidui.util.ThreadPoolUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.internal.StringUtil;
import org.jsoup.nodes.Document;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;


@Slf4j
@Component
public class Task {


    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 任务
     *
     * @param name      chain名
     * @param paramList 接口请求参数列表
     */
    public void task(String name, List<ApiRequestParam> paramList) {
        log.info(name + "任务开始");
        LocalDateTime start = LocalDateTime.now();

        // 当前时间，作为生成excel的前缀
        String dateTime = DateTimeFormatter.ofPattern("MM_dd_HH_mm").format(LocalDateTime.now());
        // 写出excel的路径
        String writePath = SystemProperties.USER_DIR + "/excel/" + dateTime + "_" + name + ".xlsx";
        // excel模板路径
        String templatePath = SystemProperties.USER_DIR + ("eth".equals(name) ? SystemProperties.TEMPLATE_PATH_ETH : SystemProperties.TEMPLATE_PATH);
        // 要将多个sheet写到同一个excel里的话，要用同一个excelWriter对象！
        ExcelWriter excelWriter = EasyExcel.write(writePath, ResponseExcel.class).withTemplate(templatePath).build();
        try {
            boolean isEth = "eth".equals(name);
            int n = isEth ? 4 : 3;
            // amount分别取500 2000 20000
            for (int i = 0; i < n; i++) {
                int amount = isEth ? 1000 : 500;
                if (i == 1) {
                    amount = isEth ? 5000 : 2000;
                } else if (i == 2) {
                    amount = isEth ? 10000 : 20000;
                } else if (i == 3) {
                    amount = 20000;
                }

                List<ResponseExcel> responseExcelList = new ArrayList<>();

                List<Future<Object>> jiaoyiduiList = new ArrayList<>();

                // 遍历交易对TransPair列表
                for (ApiRequestParam param : paramList) {
                    final int amount1 = amount;
                    Future<Object> f = ThreadPoolUtil.getThreadPool().submit(() -> {
                        String gasPrice = "1";

                        // 要写出excel的一行的内容
                        ResponseExcel responseExcel = new ResponseExcel();
                        responseExcel.setTransPair(param.getTransPair());
                        List<Future<ResponseExcel>> futureList = new ArrayList<>();
                        if (isEth) {
                            try {
                                Document document = Jsoup.connect(param.getGasUrl()).ignoreContentType(true)
                                        .header("Cookie", "_ga=GA1.2.922109069.1634187028; ASP.NET_SessionId=qupnjemci5phme4zlzhrazyw; __cflb=02DiuFnsSsHWYH8WqVXaqGvd6BSBaXQLUnveSghSS4xYU; _gid=GA1.2.87665601.1634883481; __cf_bm=6mpXF1lNQ8SMj3C1u8hjKEQPo19_elsA44euARvRTX0-1634883480-0-ASL0QD+Age/AjfsR8HJDGGeI9/ccNqz6HO7IvpjCVOU9hojt8xN/jSZsVeUg3u2yvLYsZagXz9QkOcPhzxQSS/O8qNn3vokU64fjTVLg6zRnOxb2TD5MBmVYxpHvHKEU+A==; _gat_gtag_UA_46998878_6=1")
                                        .proxy("127.0.0.1", 1080)
                                        .timeout(180000)
                                        .get();
                                String json = document.body().text();
                                Map<String, Object> map = objectMapper.readValue(json, Map.class);
                                gasPrice = (String) map.get(param.getGasKey());
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

                            param.setGasPrice(gasPrice);
                        }
                        CountDownLatch begin = new CountDownLatch(5);
                        // 遍历5个交易所
                        for (String exchange : SystemProperties.EXCHANGE_LIST) {
                            //if (!"openoceanv2".equals(exchange)) continue;
                            // 多线程，5个交易所同时跑
                            Future<ResponseExcel> future = ThreadPoolUtil.getThreadPool().submit(new Crawler(amount1, exchange, param, responseExcel, begin));
                            futureList.add(future);
                            begin.countDown();
                            //responseExcel = future.get();
                        }
                        for (Future<ResponseExcel> future : futureList) {
                            try {
                                responseExcel = future.get();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            } catch (ExecutionException e) {
                                e.printStackTrace();
                            }
                        }
                        // 计算差价
                        calculateSpreads(responseExcel);

                        log.info("pairtimes " + responseExcel.getOpenOceanV1Time() + " " + responseExcel.getOpenOceanV2Time() +
                                " " + responseExcel.getLinchTime() + " " + responseExcel.getParaswapTime() + " " + responseExcel.getMechaTime());
                        log.info("pools:poolSize:{},corePoolSize:{},activeCount:{},completedTaskCount:{}," +
                                "taskCount:{},queueSize:{},largestPoolSize:{},maximumPoolSize:{},aliveTime:{}",
                                getThreadPool().getPoolSize(), getThreadPool().getCorePoolSize(),
                                getThreadPool().getActiveCount(),getThreadPool().getCompletedTaskCount(),
                                getThreadPool().getTaskCount(),getThreadPool().getQueue().size(),
                                getThreadPool().getLargestPoolSize(),getThreadPool().getMaximumPoolSize(),
                                getThreadPool().getKeepAliveTime(TimeUnit.SECONDS));

                        // 加入待写入excel列表
                        responseExcelList.add(responseExcel);

                        return null;
                    });
                    jiaoyiduiList.add(f);
                }

                for (Future<Object> f : jiaoyiduiList) {
                    f.get();
                }

                // 每次都要创建writeSheet
                WriteSheet writeSheet = EasyExcel.writerSheet(i).build();

                for (int a = 0; a < responseExcelList.size(); a++) {
                    ResponseExcel re = responseExcelList.get(a);
                    if (StringUtil.isBlank(re.getOpenOceanV1PriceUsd()) ||
                            StringUtil.isBlank(re.getOpenOceanV2PriceUsd()) ||
                            StringUtil.isBlank(re.getLinchPriceUsd()) ||
                            StringUtil.isBlank(re.getParaswapPriceUsd()) ||
                            StringUtil.isBlank(re.getMechaPriceUsd()) ||
                            "NaN".equals(re.getOpenOceanV1PriceUsd()) ||
                            "NaN".equals(re.getOpenOceanV2PriceUsd()) ||
                            "NaN".equals(re.getLinchPriceUsd()) ||
                            "NaN".equals(re.getParaswapPriceUsd()) ||
                            "NaN".equals(re.getMechaPriceUsd())) {
                        responseExcelList.remove(a--);
                    }
                }
                // 写出
                excelWriter.fill(responseExcelList, writeSheet);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

        LocalDateTime end = LocalDateTime.now();
        Duration between = Duration.between(start, end);
        log.info(name + "任务结束，用时{}分钟", between.toMinutes());
    }

    /**
     * 计算差价
     *
     * @param responseExcel
     */
    private void calculateSpreads(ResponseExcel responseExcel) {
        // v2的price、gas
        String openOceanV2Price = responseExcel.getOpenOceanV2Price();
        String openOceanV2Gas = responseExcel.getOpenOceanV2Gas();
        String openOceanV2PriceUsd = responseExcel.getOpenOceanV2PriceUsd();
        String openOceanV2GasUsd = responseExcel.getOpenOceanV2GasUsd();
        String openoceanV2InUsd = responseExcel.getOpenOceanV2InUsd();
        // 其他的price、gas
        String openOceanV1Price = responseExcel.getOpenOceanV1Price();
        String openOceanV1Gas = responseExcel.getOpenOceanV1Gas();
        String linchPrice = responseExcel.getLinchPrice();
        String linchGas = responseExcel.getLinchGas();
        String paraswapPrice = responseExcel.getParaswapPrice();
        String paraswapPriceGas = responseExcel.getParaswapPriceGas();
        String mechaPrice = responseExcel.getMechaPrice();
        String mechaPriceGas = responseExcel.getMechaPriceGas();

        String openOceanV1PriceUsd = responseExcel.getOpenOceanV1PriceUsd();
        String openOceanV1GasUsd = responseExcel.getOpenOceanV1GasUsd();
        String linchPriceUsd = responseExcel.getLinchPriceUsd();
        String linchGasUsd = responseExcel.getLinchGasUsd();
        String paraswapPriceUsd = responseExcel.getParaswapPriceUsd();
        String paraswapPriceGasUsd = responseExcel.getParaswapPriceGasUsd();
        String mechaPriceUsd = responseExcel.getMechaPriceUsd();
        String mechaPriceGasUsd = responseExcel.getMechaPriceGasUsd();

        String openOceanV1InUsd = responseExcel.getOpenOceanV1InUsd();
        String linchInUsd = responseExcel.getLinchInUsd();
        String paraswapInUsd = responseExcel.getParaswapInUsd();
        String mechaInUsd = responseExcel.getMechaInUsd();


        // 计算两数之差
        responseExcel.setSpreadsOov1Pri(sub(openOceanV2Price, openOceanV1Price));
        responseExcel.setSpreads1inchPri(sub(openOceanV2Price, linchPrice));
        responseExcel.setSpreadsParaswapPri(sub(openOceanV2Price, paraswapPrice));
        responseExcel.setSpreadsMechaPri(sub(openOceanV2Price, mechaPrice));
        responseExcel.setSpreadsOov1Gas(sub(openOceanV2Gas, openOceanV1Gas));
        responseExcel.setSpreads1inchGas(sub(openOceanV2Gas, linchGas));
        responseExcel.setSpreadsParaswapGas(sub(openOceanV2Gas, paraswapPriceGas));
        responseExcel.setSpreadsMechaGas(sub(openOceanV2Gas, mechaPriceGas));

        responseExcel.setSpreadsOov1PriUsd(sub(openOceanV2PriceUsd, openOceanV1PriceUsd));
        responseExcel.setSpreads1inchPriUsd(sub(openOceanV2PriceUsd, linchPriceUsd));
        responseExcel.setSpreadsParaswapPriUsd(sub(openOceanV2PriceUsd, paraswapPriceUsd));
        responseExcel.setSpreadsMechaPriUsd(sub(openOceanV2PriceUsd, mechaPriceUsd));
        responseExcel.setSpreadsOov1GasUsd(sub(openOceanV2GasUsd, openOceanV1GasUsd));
        responseExcel.setSpreads1inchGasUsd(sub(openOceanV2GasUsd, linchGasUsd));
        responseExcel.setSpreadsParaswapGasUsd(sub(openOceanV2GasUsd, paraswapPriceGasUsd));
        responseExcel.setSpreadsMechaGasUsd(sub(openOceanV2GasUsd, mechaPriceGasUsd));

        responseExcel.setSpreadsOov1InUsd(sub(openoceanV2InUsd, openOceanV1InUsd));
        responseExcel.setSpreads1inchInUsd(sub(openoceanV2InUsd, linchInUsd));
        responseExcel.setSpreadsParaswapInUsd(sub(openoceanV2InUsd, paraswapInUsd));
        responseExcel.setSpreadsMechaInUsd(sub(openoceanV2InUsd, mechaInUsd));
    }

    /**
     * 计算两数之差
     *
     * @param oo2
     * @param another
     */
    private String sub(String oo2, String another) {
        if (oo2 == null || another == null) {
            return "--";
        }
        try {
            // 字符串转double再相减
//            double oo2Double = Double.parseDouble(oo2);
//            double anotherDouble = Double.parseDouble(another);
//            double d = oo2Double - anotherDouble;
//            return d + "";

            BigDecimal oo2Bd = new BigDecimal(oo2);
            BigDecimal anotherBd = new BigDecimal(another);
            BigDecimal d = oo2Bd.subtract(anotherBd);
            return d.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "--";
        }
    }
}
